package pract1modif;

import java.awt.Toolkit;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.text.DefaultCaret;


public class ChatMulticast extends javax.swing.JFrame {
    private MulticastSocket socket = null; 
    private final String address = "224.0.0.100";
    private final int socketNumber = 2345;
    private Recibir recepcion = null; //Hilo de recepcion
    private int numThread = 0; //Es un contador de los hilos de recepcion creados.
    
    public ChatMulticast() {
         try {      
            socket = new MulticastSocket(socketNumber); 
            
            //Hacemos que los datagramas enviados por nosotros no nos vuelvan. El argumento es disable=true.
            //socket.setLoopbackMode(true);
            
            socket.joinGroup(InetAddress.getByName(address));
            
        } catch (IOException e) { 
            Logger.getLogger(ChatMulticast.class.getName()).log(Level.SEVERE, null, e);
            System.exit(1); 
        }
        
	//se inicializan los elementos de la GUI
        initComponents();
        
        //Hacemos que el TextArea haga autoscroll y se vean las entradas mas recientes.
        DefaultCaret caret = (DefaultCaret)jTextArea_LecturaMensajes.getCaret();
        caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
        
        //Cambiamos el icono del jframe por uno "propio"
        setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("iconmsn.png")));
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jTextField_EscrituraMensajes = new javax.swing.JTextField();
        jBotonEscritura = new javax.swing.JButton();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTextArea_LecturaMensajes = new javax.swing.JTextArea();
        jEtiqueta1 = new javax.swing.JLabel();
        jEtiqueta2 = new javax.swing.JLabel();
        jBotonEscuchar = new javax.swing.JButton();
        jBotonEnviar = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Chat Multicast");
        setBackground(new java.awt.Color(204, 204, 204));
        setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
        setResizable(false);
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing(evt);
            }
        });

        jTextField_EscrituraMensajes.setEnabled(false);

        jBotonEscritura.setText("Start Writting");
        jBotonEscritura.setActionCommand("Start Writing");
        jBotonEscritura.setEnabled(false);
        jBotonEscritura.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jBotonEscrituraMouseClicked(evt);
            }
        });

        jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        jScrollPane1.setAutoscrolls(true);

        jTextArea_LecturaMensajes.setEditable(false);
        jTextArea_LecturaMensajes.setColumns(20);
        jTextArea_LecturaMensajes.setRows(5);
        jTextArea_LecturaMensajes.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
        jTextArea_LecturaMensajes.setEnabled(false);
        jScrollPane1.setViewportView(jTextArea_LecturaMensajes);

        jEtiqueta1.setText("Messages received:");
        jEtiqueta1.setEnabled(false);

        jEtiqueta2.setText("Type your message:");
        jEtiqueta2.setEnabled(false);

        jBotonEscuchar.setText("Start Listening");
        jBotonEscuchar.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jBotonEscucharMouseClicked(evt);
            }
        });

        jBotonEnviar.setText("Send Message");
        jBotonEnviar.setEnabled(false);
        jBotonEnviar.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jBotonEnviarMouseClicked(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane1)
                    .addComponent(jEtiqueta1)
                    .addComponent(jBotonEscuchar)
                    .addComponent(jBotonEscritura)
                    .addComponent(jEtiqueta2)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jTextField_EscrituraMensajes, javax.swing.GroupLayout.PREFERRED_SIZE, 340, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jBotonEnviar)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jBotonEscuchar)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(jEtiqueta1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 259, Short.MAX_VALUE)
                .addGap(47, 47, 47)
                .addComponent(jBotonEscritura)
                .addGap(12, 12, 12)
                .addComponent(jEtiqueta2)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(jBotonEnviar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(jTextField_EscrituraMensajes, javax.swing.GroupLayout.PREFERRED_SIZE, 63, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents
    //Funcion que nos permite ver los threads vivos en un momento determinado
    //Es una herramienta extra asociada al fichero ThreadUtility.java
    //No pertenece a la funcionalidad del chat propiamente dicha
    private void verThreads(){
        System.out.println("\n<<<<<<<<<< Threads ----------");
        new ThreadUtility().showActiveThreads();
        System.out.println("\n---------- Threads >>>>>>>>>>>");
    }
    
    
    //Funcion que realizará el envío de un mensaje pasado como parametro a través del MulticastSocket de la clase.
    private void enviar(String mensaje) throws IOException {                  
        String buf = mensaje;
        byte[] b = buf.getBytes();
        DatagramPacket packet = new DatagramPacket(b, 0, b.length, InetAddress.getByName(this.address), this.socketNumber);
        socket.send(packet);
        
        //Reseteamos el campo de textode la GUI, para poder volver a introducir un nuevo mensaje
        jTextField_EscrituraMensajes.setText("");
    }
    
    //Clase que permitirá la lectura permanente de mensajes asociados al MulticastSocket del la clase principal.
    //Se declara como un hilo para poder tener funcionalidad en paralelo del resto de la GUI.
    class Recibir extends Thread { 
        private volatile boolean done = false;
        public Recibir(String str){
                    super("_RECEPCION_MULTICAST_" + str);
	}            
        public void run() {
            byte[] buf = new byte[512]; 

            DatagramPacket dp = new DatagramPacket(buf, buf.length); //Así evitamos crear nuevos datagramPackets, evitando problemas de memoria.

            while (!Recibir.interrupted() && !done ) { 
            //while ( !done ) { 
                try { 
                    socket.receive(dp); 
                    
                    //Tenemos un paquete leido. Ahora extraemos su contenido
                    String mensajeRecibido = new String(dp.getData(), 0, dp.getLength()); 

                    //Mostramos el mensaje recibido en la GUI
                    jTextArea_LecturaMensajes.append("\n>"+mensajeRecibido+"\n");
                    
                } catch (IOException e) { 
                    Logger.getLogger(ChatMulticast.class.getName()).log(Level.SEVERE, null, e);
                } 
            }
        }
        public void setDone(){
            done = true;     
        }
    }
   
    private void jBotonEscrituraMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jBotonEscrituraMouseClicked
        String textBoton = jBotonEscritura.getText();
        if(jBotonEscritura.isEnabled()){
            if(textBoton.equalsIgnoreCase("Start Writting")){
                //Actualizamos valores de los elementos de la GUI
                jEtiqueta2.setEnabled(true);
                jTextField_EscrituraMensajes.setEnabled(true);
                jBotonEnviar.setEnabled(true);
                jBotonEscritura.setText("Stop Writting");
                jTextArea_LecturaMensajes.append("\nStarting write application\n");
            }
            if(textBoton.equalsIgnoreCase("Stop Writting")){
                //Actualizamos valores de los elementos de la GUI
                jEtiqueta2.setEnabled(false);
                jTextField_EscrituraMensajes.setEnabled(false);
                jBotonEnviar.setEnabled(false);
                jTextField_EscrituraMensajes.setText("");
                jBotonEscritura.setText("Start Writting");
                jTextArea_LecturaMensajes.append("\nStopping write application\n");
            }
        }
    }//GEN-LAST:event_jBotonEscrituraMouseClicked

    private void jBotonEscucharMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jBotonEscucharMouseClicked
        String textBoton = jBotonEscuchar.getText();

        if(textBoton.equalsIgnoreCase("Start Listening")){   
            //Actualizamos valores de los elementos de la GUI
            jEtiqueta1.setEnabled(true);
            jTextArea_LecturaMensajes.setEnabled(true);
            jBotonEscritura.setEnabled(true);
            jBotonEscuchar.setText("Stop Listening");
            
            jTextArea_LecturaMensajes.setText("Starting application...\nIn few seconds you will listen messages.\nClick on Start Writting button to start sending messages.\n\n");

            //Creamos un nuevo hilo de recepcion y lo arrancamos
            this.recepcion = new Recibir(Integer.toString(this.numThread++)); 
            this.recepcion.start();
        }
        if(textBoton.equalsIgnoreCase("Stop Listening")){
            //Actualizamos valores de los elementos de la GUI
            jTextArea_LecturaMensajes.setEnabled(false);
            jBotonEscritura.setEnabled(false);
            jBotonEnviar.setEnabled(false);
            jEtiqueta2.setEnabled(false);
            jEtiqueta1.setEnabled(false);
            jTextField_EscrituraMensajes.setEnabled(false);
            jBotonEscuchar.setText("Start Listening");
            jBotonEscritura.setText("Start Writting");
            
            //paramos el hilo de recepcion de mensajes
            //this.recepcion.stop();
            this.recepcion.setDone();
            this.recepcion.interrupt();

            jTextArea_LecturaMensajes.setText("\n Application stopped.\n ");
        }

    }//GEN-LAST:event_jBotonEscucharMouseClicked

    private void jBotonEnviarMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jBotonEnviarMouseClicked
       String mensaje = jTextField_EscrituraMensajes.getText();
        if(mensaje.length()<512){
            if(mensaje.length()>0){
                
                try {
                    enviar(mensaje); 
                } 
                catch (IOException ex){
                    Logger.getLogger(ChatMulticast.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
        else{
            jTextArea_LecturaMensajes.append("\n>>>> Application Error: Message too long. Messages cannot exceed 512 characteres.\n ");
            jTextField_EscrituraMensajes.setText("");
        }
        
        //Mostramos por consola los threads activos.
        //Esta funcionalidad es extra, y sirve para controlar si run() de la clase Recibir se termina correctamente tras pulsar el boton de Stop Listening
        //Por tanto, si estamos escuchando mensajes debe aparecer en el listado de threads una entrada del estilo _RECEPCION_MULTICAST_X (siendo X un entero)
        //En caso de no estar escuchando no debería aparecer ningun hilo con esa nomenclatura.
        this.verThreads();
    }//GEN-LAST:event_jBotonEnviarMouseClicked

    private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
        //if(this.recepcion!=null){
        if(this.recepcion!=null && this.recepcion.isAlive()){
            //Paramos el hilo de recepcion de mensajes
            this.recepcion.setDone();
            this.recepcion.interrupt();

            jTextArea_LecturaMensajes.setText("\n Application stopped.\n ");
            
            //Esperamos a que el thread esté terminado
            while(this.recepcion.isAlive()){
            //System.out.println("\n\t\t\t----------\n\t\tPARANDO THREAD\n\t\t\t----------");
            }
            System.out.println("\n\t\t\t----------\n\t\tTHREAD PARADO\n\t\t\t----------");
        }
        try {
            //Salimos del grupo Multicast
            socket.leaveGroup(InetAddress.getByName(address));
            
            //Cerramos el socket antes de terminar la aplicacion.
            socket.close();
            
            System.out.println("\n\t\t\t----------\n\t\tSOCKET CERRADO\n\t\t\t----------");
            
        } catch (UnknownHostException ex) {
            Logger.getLogger(ChatMulticast.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException iex) {
            Logger.getLogger(ChatMulticast.class.getName()).log(Level.SEVERE, null, iex);
        }
    }//GEN-LAST:event_formWindowClosing

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                //if ("Nimbus".equals(info.getName())) {
                if ("Windows".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(ChatMulticast.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(ChatMulticast.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(ChatMulticast.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(ChatMulticast.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
            new ChatMulticast().setVisible(true);
            }
        });

        
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton jBotonEnviar;
    private javax.swing.JButton jBotonEscritura;
    private javax.swing.JButton jBotonEscuchar;
    private javax.swing.JLabel jEtiqueta1;
    private javax.swing.JLabel jEtiqueta2;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTextArea jTextArea_LecturaMensajes;
    private javax.swing.JTextField jTextField_EscrituraMensajes;
    // End of variables declaration//GEN-END:variables
}
